package objects;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.LinkedHashMap;
import java.util.Map;

import sections.ISection.Section;

import static objects.AdapletConstants.sNewLine;

public class Type implements IObject, IHasAttributes
{
	/**
	 * 
	 */
	private static final long serialVersionUID = -2776642963689896438L;
	private String fTypeName;
	private String fTableName;

	// private boolean fIsAbstract;

	private String fDerives;

	// Basics
	private String fID;
	private boolean fIdIsMapped;
	private String fType;
	private String fTypeAlias;
	private String fName;
	private String fRevision;
	private String fPreviousRevision;
	private String fNextRevision;
	private String fPolicy;
	private String fState;
	private String fDescription;
	private String fOwner;
	private String fOriginated;
	private String fModified;

	// <Attribute Name, Column Name>
	private Map<String, String> fAttributeList;

	public Type(String typeName)
	{
		fTypeName = typeName;
		fTableName = "";

		// fIsAbstract = false;
		fDerives = "";

		fID = "";
		setIdIsMapped(true);
		fType = "";
		fTypeAlias = "";
		fName = "";
		fRevision = "";
		fPreviousRevision = "";
		fNextRevision = "";
		fPolicy = "";
		fState = "";
		fDescription = "";
		fOwner = "";
		fOriginated = "";
		fModified = "";

		fAttributeList = new LinkedHashMap<String, String>();
	}

	public String buildTypeDefinition()
	{
		StringBuffer typeDef = new StringBuffer(1000);

		// Comment
		typeDef.append(buildComment());

		// Type Declaration
		typeDef.append("type \"");
		typeDef.append(fTypeName);
		typeDef.append("\" {");
		typeDef.append(sNewLine);

		// Derives
		if (fDerives != null && !fDerives.isEmpty())
		{
			typeDef.append("\tderive \"");
			typeDef.append(fDerives);
			typeDef.append("\";");
			typeDef.append(sNewLine);
		}

		// Id
		typeDef.append("\tid ");
		typeDef.append(fTableName);
		typeDef.append("(");
		typeDef.append(fID);
		typeDef.append(")");
		if (fIdIsMapped)
		{
			typeDef.append(" mapped");
		}
		typeDef.append(";");
		typeDef.append(sNewLine);

		// Type
		typeDef.append("\ttype ");
		typeDef.append(fType);
		if (fTypeAlias != null && !fTypeAlias.isEmpty())
		{
			typeDef.append(" alias \"");
			typeDef.append(fTypeAlias);
			typeDef.append("\"");
		}
		typeDef.append(";");
		typeDef.append(sNewLine);

		// Name
		typeDef.append("\tname ");
		typeDef.append(fName);
		typeDef.append(";");
		typeDef.append(sNewLine);

		// Revision
		typeDef.append("\trevision ");
		typeDef.append(fRevision);
		typeDef.append(";");
		typeDef.append(sNewLine);

		if (fPreviousRevision != null && !fPreviousRevision.isEmpty())
		{
			typeDef.append("\tprevrev ");
			typeDef.append(fPreviousRevision);
			typeDef.append(";");
			typeDef.append(sNewLine);
		}

		if (fNextRevision != null && !fNextRevision.isEmpty())
		{
			typeDef.append("\tnextrev ");
			typeDef.append(fNextRevision);
			typeDef.append(";");
			typeDef.append(sNewLine);
		}

		// Policy
		typeDef.append("\tpolicy ");
		typeDef.append(fPolicy);
		typeDef.append(";");
		typeDef.append(sNewLine);

		// State
		typeDef.append("\tstate ");
		typeDef.append(fState);
		typeDef.append(";");
		typeDef.append(sNewLine);

		// Description
		if (fDescription != null && !fDescription.isEmpty())
		{
			typeDef.append("\tdescription ");
			typeDef.append(fDescription);
			typeDef.append(";");
			typeDef.append(sNewLine);
		}

		// Owner
		typeDef.append("\towner ");
		typeDef.append(fOwner);
		typeDef.append(";");
		typeDef.append(sNewLine);

		// Originated
		if (fOriginated != null && !fOriginated.isEmpty())
		{
			typeDef.append("\tcreated ");
			typeDef.append(fOriginated);
			typeDef.append(";");
			typeDef.append(sNewLine);
		}

		// Modified
		if (fModified != null && !fModified.isEmpty())
		{
			typeDef.append("\tmodified ");
			typeDef.append(fModified);
			typeDef.append(";");
			typeDef.append(sNewLine);
		}

		// Attributes
		for (String attributeName : fAttributeList.keySet())
		{
			String columnName = fAttributeList.get(attributeName);

			typeDef.append("\tattribute \"");
			typeDef.append(attributeName);
			typeDef.append("\" ");
			typeDef.append(columnName);
			typeDef.append(";");
			typeDef.append(sNewLine);
		}

		typeDef.append("}");
		typeDef.append(sNewLine);

		return typeDef.toString();
	}

	public String getTypeName()
	{
		return fTypeName;
	}

	public void setTypeName(String fTypeName)
	{
		this.fTypeName = fTypeName;
	}

	public String getTableName()
	{
		return fTableName;
	}

	public void setTableName(String fTableName)
	{
		this.fTableName = fTableName;
	}

	public String getDerives()
	{
		return fDerives;
	}

	public void setDerives(String fDerives)
	{
		this.fDerives = fDerives;
	}

	public String getID()
	{
		return fID;
	}

	public void setID(String fID)
	{
		this.fID = fID;
	}

	public String getType()
	{
		return fType;
	}

	public void setType(String fType)
	{
		this.fType = fType;
	}

	public String getTypeAlias()
	{
		return fTypeAlias;
	}

	public void setTypeAlias(String fTypeAlias)
	{
		this.fTypeAlias = fTypeAlias;
	}

	public String getName()
	{
		return fName;
	}

	public void setName(String fName)
	{
		this.fName = fName;
	}

	public String getRevision()
	{
		return fRevision;
	}

	public void setRevision(String fRevision)
	{
		this.fRevision = fRevision;
	}

	public String getPreviousRevision()
	{
		return fPreviousRevision;
	}

	public void setPreviousRevision(String fPreviousRevision)
	{
		this.fPreviousRevision = fPreviousRevision;
	}

	public String getNextRevision()
	{
		return fNextRevision;
	}

	public void setNextRevision(String fNextRevision)
	{
		this.fNextRevision = fNextRevision;
	}

	public String getPolicy()
	{
		return fPolicy;
	}

	public void setPolicy(String fPolicy)
	{
		this.fPolicy = fPolicy;
	}

	public String getState()
	{
		return fState;
	}

	public void setState(String fState)
	{
		this.fState = fState;
	}

	public String getDescription()
	{
		return fDescription;
	}

	public void setDescription(String fDescription)
	{
		this.fDescription = fDescription;
	}

	public String getOwner()
	{
		return fOwner;
	}

	public void setOwner(String fOwner)
	{
		this.fOwner = fOwner;
	}

	public String getOriginated()
	{
		return fOriginated;
	}

	public void setOriginated(String fOriginated)
	{
		this.fOriginated = fOriginated;
	}

	public String getModified()
	{
		return fModified;
	}

	public void setModified(String fModified)
	{
		this.fModified = fModified;
	}

	public void addAttribute(String attributeName, String columnName)
	{
		fAttributeList.put(attributeName, columnName);
	}

	@Override
	public String toString()
	{
		return buildTypeDefinition();
	}

	private String buildComment()
	{
		StringBuffer commentBuf = new StringBuffer(200);

		commentBuf.append("#############################################");
		commentBuf.append(sNewLine);

		commentBuf.append("# Type Definition: \"");
		commentBuf.append(fTypeName);
		commentBuf.append("\"");
		commentBuf.append(sNewLine);

		commentBuf.append("# Using Table: ");
		commentBuf.append(fTableName);
		commentBuf.append(sNewLine);

		commentBuf.append("#############################################");
		commentBuf.append(sNewLine);

		return commentBuf.toString();
	}

	public void setIdIsMapped(boolean fIdIsMapped)
	{
		this.fIdIsMapped = fIdIsMapped;
	}

	public boolean isIdIsMapped()
	{
		return fIdIsMapped;
	}

	@Override
	public Section getSectionType()
	{
		return Section.TYPE;
	}

	@Override
	public String getObjectName()
	{
		return fTypeName;
	}

	public String[] getAttributeNames()
	{
		return fAttributeList.keySet().toArray(new String[0]);
	}

	public String getAttributeColumn(String attribute)
	{
		return fAttributeList.get(attribute);

	}
	
	private void readObject(ObjectInputStream iStream) throws IOException, ClassNotFoundException
	{
		iStream.defaultReadObject();
		validateState();
	}
	
	private void writeObject(ObjectOutputStream oStream) throws IOException
	{
		oStream.defaultWriteObject();
	}
	
	private void validateState()
	{
		if(fType == null || fTypeName.isEmpty())
		{
			throw new IllegalArgumentException("");
		}
		else if(fTableName == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fDerives == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fID == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fType == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fTypeAlias == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fName == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fRevision == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fPreviousRevision == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fNextRevision == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fPolicy == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fState == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fDescription == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fOwner == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fOriginated == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fModified == null)
		{
			throw new IllegalArgumentException("");
		}
		else if(fAttributeList == null)
		{
			throw new IllegalArgumentException("");
		}
	}

	@Override
	public void removeAttribute(String attrName)
	{
		fAttributeList.remove(attrName);
	}
}
